Running principal component and k-means clustering analyses
dir.create("./results", showWarnings = F)
dir.create("./results/figures", showWarnings = F)
source("./src/GM_stats.R")
Performing GPA
|
| | 0%
|
|======================== | 25%
|
|================================================ | 50%
|
|======================================================================== | 75%
|
|================================================================================================| 100%
Making projections... Finished!



[1] -0.9999859
[1] 0.9999956
[1] -0.9999535
[1] -0.9999967
[1] -0.999442
[1] -0.9999388
[1] -1
[1] 0.9997545
[1] -0.9996835
[1] 0.9999969

Plotting PC and k-means results
K-means clustering plot:

Kennel-club groupings and PC results:
Too few points to calculate an ellipse

Discriminant Factors between groups
Comparison of landmarks between group means
# Extracting first 5 PCs and each kennel-club grouping scheme
skulls <- cbind(SlicerMorph.repc[, 1:5],
"UKC" = factor(SlicerMorph.repc$UKC, ordered = T, levels = UKC_group_order),
"AKC" = factor(SlicerMorph.repc$AKC, ordered = T, levels = AKC_group_order),
"bitework" = factor(SlicerMorph.repc$Bitework),
"scentwork" = factor(SlicerMorph.repc$Nosework))
skulls <- na.omit(skulls)
# Create a paired and ordered GPA-grouping object
mixdrop<-gpa$coords[,,order(dimnames(gpa$coords)[[3]])]
mixdrop<-mixdrop[,,-96]
reforge<-geomorph.data.frame(shape = mixdrop,
UKC = na.omit(factor(SlicerMorph.repc$UKC, ordered = T)),
AKC = na.omit(factor(SlicerMorph.repc$AKC, ordered = T)),
bitework = na.omit(factor(SlicerMorph.repc$Bitework[-96])),
scentwork = na.omit(factor(SlicerMorph.repc$Nosework[-96])))
# Create a function that subsets the mean landmarks for each group and does a pairwise subtraction and squaring of those means
meanforge <- function(target, group) {
#sub-setting the means and sending them to a new object
new.coords<-coords.subset(target[["shape"]], group = target[[group]])
output_means<-lapply(new.coords, mshape)
#perform the subtraction and squaring of the pairwise combinations
result<- combn(output_means, 2, function(x) (x[[1]]-x[[2]])^2, simplify = FALSE)
#rename the pairs so they're readable
names(result)<-combn(names(output_means), 2, function(n) paste(n[1], "-", n[2]), simplify = TRUE)
return(result)
}
#now run the function for each grouping scheme. Each member of the list will be named based on which two groups are compared
meanshape.UKC<-meanforge(reforge,"UKC")
meanshape.AKC<-meanforge(reforge,"AKC")
meanshape.nose<-meanforge(reforge,"scentwork")
meanshape.bite<-meanforge(reforge,"bitework")
Reformatting data for visualization:
calc_gpa_dist <- function(dat){
require(tidyr)
n <- length(names(dat))
results <- matrix(NA, nrow = nrow(dat[[1]]), ncol = n)
results <- as.data.frame(results)
results <- cbind(seq(1,nrow(results)), results)
colnames(results) <- c("landmark",names(dat))
for(i in names(dat)){
results[[i]] <- sqrt(dat[[i]][,"X"]^2 + dat[[i]][,"Y"]^2 + dat[[i]][,"Z"]^2)
}
results_long <- pivot_longer(results, cols = -"landmark", names_to = "comparison")
results_long$landmark <- factor(results_long$landmark)
return(results_long)
}
meanshape.AKC[["plotting"]] <- calc_gpa_dist(meanshape.AKC)
meanshape.UKC[["plotting"]] <- calc_gpa_dist(meanshape.UKC)
meanshape.bite[["plotting"]] <- calc_gpa_dist(meanshape.bite)
meanshape.bite[["cutoff"]] <- data.frame("mean" = mean(meanshape.bite[["plotting"]]$value),
"sd" = sd(meanshape.bite[["plotting"]]$value))
meanshape.nose[["plotting"]] <- calc_gpa_dist(meanshape.nose)
meanshape.nose[["cutoff"]] <- data.frame("mean" = mean(meanshape.nose[["plotting"]]$value),
"sd" = sd(meanshape.nose[["plotting"]]$value))
meanshape.AKC[["plotting"]]$short <- ifelse(grepl("toy", meanshape.AKC[["plotting"]]$comparison,
ignore.case = T),
"toy",
ifelse(grepl("natural", meanshape.AKC[["plotting"]]$comparison,
ignore.case = T),
"natural", "other"))
meanshape.AKC[["plotting"]]$short <- factor(meanshape.AKC[["plotting"]]$short,
ordered = T, levels = c("toy", "natural", "other"))
meanshape.AKC[["cutoff"]] <- data.frame("mean" = mean(meanshape.AKC[["plotting"]]$value),
"sd" = sd(meanshape.AKC[["plotting"]]$value))
meanshape.UKC[["plotting"]]$short <- ifelse(grepl("companion", meanshape.UKC[["plotting"]]$comparison,
ignore.case = T),
"companion",
ifelse(grepl("natural", meanshape.UKC[["plotting"]]$comparison,
ignore.case = T),
"natural", "other"))
meanshape.UKC[["plotting"]]$short <- factor(meanshape.UKC[["plotting"]]$short,
ordered = T, levels = c("companion", "natural", "other"))
meanshape.UKC[["cutoff"]] <- data.frame("mean" = mean(meanshape.UKC[["plotting"]]$value),
"sd" = sd(meanshape.UKC[["plotting"]]$value))
AKC & UKC groups:
p_UKC_compare <- ggplot(meanshape.UKC[["plotting"]], aes(landmark, value, color = short)) +
geom_point(position = position_jitter(width = 0.1)) +
geom_hline(yintercept = meanshape.UKC[["cutoff"]]$mean+2*meanshape.UKC[["cutoff"]]$sd,
lty = 2) +
scale_color_viridis_d(name = "Comparison\nagainst:") +
annotate("segment", x = 1, xend = 20, y = 0.0085, yend = 0.0085,
lty = 1, color = "red") +
annotate("text", x = 10, y = 0.0075, label = "Dorsal features", color = "red") +
annotate("segment", x = 21, xend = 38, y = 0.0085, yend = 0.0085,
lty = 1, color = "blue") +
annotate("text", x = 30, y = 0.0075, label = "Ventral features", color = "blue") +
ylab("Group-mean landmark\ndistance squared") +
ggtitle("UKC group comparisons") +
theme_bw() #+
#theme(legend.position = "bottom")
#theme(legend.position = "inside", legend.position.inside = c(0.75,0.65),
# legend.background = element_rect(color = "gray30", fill = "white", linetype="solid",
# linewidth = 0.2))
p_AKC_compare <- ggplot(meanshape.AKC[["plotting"]], aes(landmark, value, color = short)) +
geom_point(position = position_jitter(width = 0.1)) +
geom_hline(yintercept = meanshape.AKC[["cutoff"]]$mean+2*meanshape.AKC[["cutoff"]]$sd,
lty = 2) +
scale_color_viridis_d(name = "Comparison\nagainst:") +
annotate("segment", x = 1, xend = 20, y = 0.0085, yend = 0.0085,
lty = 1, color = "red") +
annotate("text", x = 10, y = 0.0075, label = "Dorsal features", color = "red") +
annotate("segment", x = 21, xend = 38, y = 0.0085, yend = 0.0085,
lty = 1, color = "blue") +
annotate("text", x = 30, y = 0.0075, label = "Ventral features", color = "blue") +
ylab("Group-mean landmark\ndistance squared") +
ggtitle("AKC group comparisons") +
theme_bw() #+
#theme(legend.position = "bottom")
#theme(legend.position = "inside", legend.position.inside = c(0.75,0.65),
# legend.background = element_rect(color = "gray30", fill = "white", linetype="solid",
# linewidth = 0.2))
p_AKC_compare / p_UKC_compare

Nosework and Bitework:
p_bite_compare <- ggplot(meanshape.bite[["plotting"]], aes(landmark, value, color = comparison)) +
geom_point(position = position_jitter(width = 0.1)) +
geom_hline(yintercept = meanshape.bite[["cutoff"]]$mean+2*meanshape.bite[["cutoff"]]$sd,
lty = 2) +
annotate("segment", x = 1, xend = 20, y = 0.007, yend = 0.007,
lty = 1, color = "red") +
annotate("text", x = 10, y = 0.0065, label = "Dorsal features", color = "red") +
annotate("segment", x = 21, xend = 38, y = 0.007, yend = 0.007,
lty = 1, color = "blue") +
annotate("text", x = 30, y = 0.0065, label = "Ventral features", color = "blue") +
scale_color_viridis_d(name = "Group-group\ncomparison") +
ylab("Group-mean landmark\ndistance squared") +
ggtitle("Bite-work group comparisons") +
theme_bw()
p_nose_compare <- ggplot(meanshape.nose[["plotting"]], aes(landmark, value, color = comparison)) +
geom_point(position = position_jitter(width = 0.1)) +
geom_hline(yintercept = meanshape.nose[["cutoff"]]$mean+2*meanshape.nose[["cutoff"]]$sd,
lty = 2) +
annotate("segment", x = 1, xend = 20, y = 0.007, yend = 0.007,
lty = 1, color = "red") +
annotate("text", x = 10, y = 0.0065, label = "Dorsal features", color = "red") +
annotate("segment", x = 21, xend = 38, y = 0.007, yend = 0.007,
lty = 1, color = "blue") +
annotate("text", x = 30, y = 0.0065, label = "Ventral features", color = "blue") +
scale_color_viridis_d(name = "Group-group\ncomparison") +
ylab("Group-mean landmark\ndistance squared") +
ggtitle("Scent-work group comparisons") +
theme_bw()
p_bite_compare / p_nose_compare + plot_layout(guides = "collect")

MANOVA
man_UKC <- manova(as.matrix(skulls[,1:5])~skulls$UKC)
summary.aov(man_UKC, test="Pillai")
Response PC.1 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.65020 0.081275 35.372 < 2.2e-16 ***
Residuals 107 0.24585 0.002298
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Response PC.2 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.073427 0.0091783 6.0866 1.902e-06 ***
Residuals 107 0.161352 0.0015080
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Response PC.3 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.016265 0.00203313 2.4906 0.01612 *
Residuals 107 0.087348 0.00081633
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Response PC.4 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.024974 0.00312178 5.7231 4.625e-06 ***
Residuals 107 0.058365 0.00054547
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Response PC.5 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.000574 0.00007179 0.1633 0.9951
Residuals 107 0.047029 0.00043952
#using Geomorph and doing pairwise comparisons
manUKC<-procD.lm(shape ~ UKC, data = reforge, RRPP = TRUE)
manAKC<-procD.lm(shape ~ AKC, data = reforge, RRPP = TRUE)
manBite<-procD.lm(shape ~ bitework, data = reforge, RRPP = TRUE)
manScent<-procD.lm(shape ~ scentwork, data = reforge, RRPP = TRUE)
hold.ukc<-manova(manUKC)
hold.akc<-manova(manAKC)
hold.bite<-manova(manBite)
hold.nose<-manova(manScent)
ukcpairs<-pairwise(manUKC, groups = reforge$UKC)
akcpairs<-pairwise(manAKC, groups = reforge$AKC)
bitepairs<-pairwise(manBite, groups = reforge$bitework)
nosepairs<-pairwise(manScent, groups = reforge$scentwork)
pvals.UKC<-summary(ukcpairs, test.type= "dist", confidence = 0.95, stat.table = TRUE)
pvals.UKC<-pvals.UKC$pairwise.tables$P[lower.tri(pvals.UKC$pairwise.tables$P)]
pvals.AKC<-summary(akcpairs, test.type= "dist", confidence = 0.95, stat.table = TRUE)
pvals.AKC<-pvals.AKC$pairwise.tables$P[lower.tri(pvals.AKC$pairwise.tables$P)]
pvals.bite<-summary(bitepairs, test.type= "dist", confidence = 0.95, stat.table = TRUE)
pvals.bite<-pvals.bite$pairwise.tables$P[lower.tri(pvals.bite$pairwise.tables$P)]
pvals.nose<-summary(nosepairs, test.type= "dist", confidence = 0.95, stat.table = TRUE)
pvals.nose<-pvals.nose$pairwise.tables$P[lower.tri(pvals.nose$pairwise.tables$P)]
pvals.ukc.adjusted<-p.adjust(pvals.UKC, method = "bonf")
pvals.akc.adjusted<-p.adjust(pvals.AKC, method = "bonf")
pvals.bite.adjusted<-p.adjust(pvals.bite, method = "bonf")
pvals.nose.adjusted<-p.adjust(pvals.nose, method = "bonf")
lda_UKC <- MASS::lda(skulls$UKC~as.matrix(skulls[,1:5]))
lda_UKC
Call:
lda(skulls$UKC ~ as.matrix(skulls[, 1:5]))
Prior probabilities of groups:
NATURAL Herding Terrier Companion Gun dog Guardian Northern Breed
0.19827586 0.23275862 0.08620690 0.12068966 0.05172414 0.09482759 0.02586207
Sighthound Scenthound
0.14655172 0.04310345
Group means:
as.matrix(skulls[, 1:5])PC.1 as.matrix(skulls[, 1:5])PC.2 as.matrix(skulls[, 1:5])PC.3
NATURAL -0.11869015 -0.019063433 0.04817564
Herding -0.12101159 0.026436494 0.04577986
Terrier -0.05852371 0.001880539 0.04485686
Companion 0.09983210 0.018101105 0.02331451
Gun dog -0.11168214 0.041234328 0.04624008
Guardian -0.06180271 0.065382213 0.06595086
Northern Breed -0.06497801 0.049661060 0.06220728
Sighthound -0.15480497 0.029842740 0.02964807
Scenthound -0.12915491 0.049368307 0.04321946
as.matrix(skulls[, 1:5])PC.4 as.matrix(skulls[, 1:5])PC.5
NATURAL -0.016530397 0.1158473
Herding 0.013615782 0.1190599
Terrier 0.032376419 0.1152870
Companion 0.011348484 0.1182048
Gun dog 0.007520818 0.1204955
Guardian 0.003014570 0.1207284
Northern Breed -0.017512289 0.1170088
Sighthound 0.018704155 0.1147247
Scenthound 0.010813419 0.1131374
Coefficients of linear discriminants:
LD1 LD2 LD3 LD4 LD5
as.matrix(skulls[, 1:5])PC.1 21.465233 2.072852 2.157478 1.232714 0.61490553
as.matrix(skulls[, 1:5])PC.2 2.130815 -20.151953 14.104686 -8.508683 1.48405135
as.matrix(skulls[, 1:5])PC.3 -13.131521 1.216403 21.217452 26.189753 4.17032936
as.matrix(skulls[, 1:5])PC.4 10.561161 -31.562092 -23.132983 17.258716 -0.02015756
as.matrix(skulls[, 1:5])PC.5 2.636558 -2.368104 6.558913 4.347214 -46.98480500
Proportion of trace:
LD1 LD2 LD3 LD4 LD5
0.7331 0.1664 0.0818 0.0172 0.0015
Testing of Functional Groups


bite_yes_canine <- bite_data$canine[bite_data$Bitework == "yes"]
bite_no_canine <- bite_data$canine[bite_data$Bitework == "no"]
shapiro.test(bite_yes_canine)
Shapiro-Wilk normality test
data: bite_yes_canine
W = 0.95076, p-value = 0.5726
shapiro.test(bite_no_canine)
Shapiro-Wilk normality test
data: bite_no_canine
W = 0.97244, p-value = 0.08128
t.test(bite_yes_canine, bite_no_canine)
Welch Two Sample t-test
data: bite_yes_canine and bite_no_canine
t = -0.30306, df = 36.495, p-value = 0.7636
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-9.152725 6.771939
sample estimates:
mean of x mean of y
57.78286 58.97325
bite_yes_carnassial <- bite_data$carnassial[bite_data$Bitework == "yes"]
bite_no_carnassial <- bite_data$carnassial[bite_data$Bitework == "no"]
shapiro.test(bite_yes_carnassial)
Shapiro-Wilk normality test
data: bite_yes_carnassial
W = 0.92608, p-value = 0.2687
shapiro.test(bite_no_carnassial)
Shapiro-Wilk normality test
data: bite_no_carnassial
W = 0.96684, p-value = 0.03577
t.test(bite_yes_carnassial, bite_no_carnassial)
Welch Two Sample t-test
data: bite_yes_carnassial and bite_no_carnassial
t = -0.52911, df = 42.805, p-value = 0.5995
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-9.417946 5.503589
sample estimates:
mean of x mean of y
57.12857 59.08575
bite_natural_canine <- bite_data$canine[bite_data$Bitework == "NATURAL"]
shapiro.test(bite_natural_canine)
Shapiro-Wilk normality test
data: bite_natural_canine
W = 0.91337, p-value = 0.2672
bite_fox_canine <- bite_data$canine[bite_data$Bitework == "FOX"]
shapiro.test(bite_fox_canine)
Shapiro-Wilk normality test
data: bite_fox_canine
W = 0.82835, p-value = 0.03197
t.test(bite_yes_canine, bite_natural_canine)
Welch Two Sample t-test
data: bite_yes_canine and bite_natural_canine
t = -1.6371, df = 13.462, p-value = 0.1248
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-29.241240 3.979681
sample estimates:
mean of x mean of y
57.78286 70.41364
t.test(bite_yes_canine, bite_fox_canine)
Welch Two Sample t-test
data: bite_yes_canine and bite_fox_canine
t = -0.85342, df = 10.859, p-value = 0.4119
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-29.77613 13.15585
sample estimates:
mean of x mean of y
57.78286 66.09300
For the paper:
Figure 2: k-means cluster on PC plot

Figure 3: UKC comparison figure
Too few points to calculate an ellipse

Too few points to calculate an ellipse
Figure 4: AKC comparison figure

Figure 5: task-specific group results

LS0tCnRpdGxlOiAiR2VvbW9ycGhpYyBNb3JwaG9tZXRyaWNzIG9uIGNhbmlkIHNrdWxscyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZmlnLndpZHRoID0gOC41KQpvcHRpb25zKGtuaXRyLmdyYXBoaWNzLmVycm9yID0gRkFMU0UpCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykKb3B0aW9ucyhyZ2wudXNlTlVMTD1UUlVFKSAjIE5vdGU6IE9wZW5HTCBpcyBkZXByZWNpYXRlZCBvbiBNYWNzIGFuZCByZXF1aXJlcyBYUXVhcnR6IGluIG9yZGVyIHRvIHdvcmsgcHJvcGVybHkuIFRoaXMgd2lsbCBjYXVzZSBhbiBlcnJvciB3aGVuIGxvYWRpbmcgdGhlIE1vcnBobyBhbmQgZ2VvbW9ycGggcGFja2FnZXMuIFNldHRpbmcgdGhpcyBvcHRpb24gcmVtb3ZlcyB0aGUgZXJyb3IuIElmIHlvdSBoYXZlIFhRdWFydHogYW5kIGRvbid0IG1pbmQgaXQgb3BlbmluZywgc3VwcHJlc3MgdGhpcyBsaW5lIG9mIGNvZGUgdG8gcmVnYWluIGZ1bGwgZnVuY3Rpb25hbGl0eS4KCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoZ3Rvb2xzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShnZW9tb3JwaCkKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ1NsaWNlck1vcnBoL1NsaWNlck1vcnBoUicpCmxpYnJhcnkoU2xpY2VyTW9ycGhSKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigibGluZHNheXdhbGRyb3AvbXVuY2hjb2xvcnMiKQpsaWJyYXJ5KG11bmNoY29sb3JzKQpgYGAKCiMjIFJ1bm5pbmcgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmQgay1tZWFucyBjbHVzdGVyaW5nIGFuYWx5c2VzCgpgYGB7cn0KZGlyLmNyZWF0ZSgiLi9yZXN1bHRzIiwgc2hvd1dhcm5pbmdzID0gRikKZGlyLmNyZWF0ZSgiLi9yZXN1bHRzL2ZpZ3VyZXMiLCBzaG93V2FybmluZ3MgPSBGKQpzb3VyY2UoIi4vc3JjL0dNX3N0YXRzLlIiKQpgYGAKCiMjIFBsb3R0aW5nIFBDIGFuZCBrLW1lYW5zIHJlc3VsdHMKCgpgYGB7ciBwbG90dGluZy1zZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBTZXR0aW5nIFVLQyBncm91cCBvcmRlcgpVS0NfZ3JvdXBfb3JkZXIgPC0gYygiTkFUVVJBTCIsICJIZXJkaW5nIiwgIlRlcnJpZXIiLCAiQ29tcGFuaW9uIiwgIkd1biBkb2ciLCAKICAgICAgICAgICAgICAgICAgICAgIkd1YXJkaWFuIiwgIk5vcnRoZXJuIEJyZWVkIiwgIlNpZ2h0aG91bmQiLCAiU2NlbnRob3VuZCIpCkFLQ19ncm91cF9vcmRlciA8LSBjKCJOQVRVUkFMIiwgIkhlcmRpbmciLCAiVGVycmllciIsICJUb3kiLCAiU3BvcnRpbmciLCAKICAgICAgICAgICAgICAgICAgICAgIldvcmtpbmciLCAiTm9uLXNwb3J0aW5nIiwgIkhvdW5kIikKCiMgZ3JvdXBpbmcgcGxvdHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEZpcnN0IGFkZGVkIHlvdXIgb3JkZXJlZCBjbGFzc2lmaWVycyBvbnRvIHRoZSBQQ0EgZGF0YQpTbGljZXJNb3JwaC5yZXBjJFVLQyA8LSBjbHNmX3Jlb3JkJFVLQy5icmVlZGluZy5TdGFuZGFyZApTbGljZXJNb3JwaC5yZXBjJEFLQyA8LSBjbHNmX3Jlb3JkJEFLQy5icmVlZGluZy5zdGFuZGFyZAojIFNldCBtaXhlZCBicmVlZCB0byBOQSBmb3IgcGxvdHRpbmcsIHRoZXJlIGlzIG9ubHkgb25lIQpTbGljZXJNb3JwaC5yZXBjJFVLQ1tTbGljZXJNb3JwaC5yZXBjJFVLQyA9PSAiTUlYRUQiXSA8LSBOQQpTbGljZXJNb3JwaC5yZXBjJEFLQ1tTbGljZXJNb3JwaC5yZXBjJEFLQyA9PSAiTUlYRUQiXSA8LSBOQQojIFNldHRpbmcgZm94IHRvIG5hdHVyYWwgZm9yIEFLQy9VS0MgZ3JvdXBpbmcgdG8gc2ltcGxpZnkgcGxvdHMKU2xpY2VyTW9ycGgucmVwYyRVS0NbU2xpY2VyTW9ycGgucmVwYyRVS0MgPT0gIkZPWCJdIDwtICJOQVRVUkFMIgpTbGljZXJNb3JwaC5yZXBjJEFLQ1tTbGljZXJNb3JwaC5yZXBjJEFLQyA9PSAiRk9YIl0gPC0gIk5BVFVSQUwiCgojIENoYW5naW5nIFllcyB0byBsb3dlcmNhc2UKU2xpY2VyTW9ycGgucmVwYyROb3Nld29yayA8LSBpZmVsc2UoY2xzZl9yZW9yZCROb3NlID09IlllcyIgfCBjbHNmX3Jlb3JkJE5vc2UgPT0gIm5vIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvbG93ZXIoY2xzZl9yZW9yZCROb3NlKSwgY2xzZl9yZW9yZCROb3NlKQojIENoYW5naW5nIE5vc2V3b3JrIHRvIG9yZGVyZWQgZmFjdG9yClNsaWNlck1vcnBoLnJlcGMkTm9zZXdvcmsgPC0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkTm9zZXdvcmssIG9yZGVyZWQgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygieWVzIiwgIm5vIiwgIk5BVFVSQUwiLCAiRk9YIikpCiMgQ2hhbmdpbmcgWWVzIHRvIGxvd2VyY2FzZSAKU2xpY2VyTW9ycGgucmVwYyRCaXRld29yayA8LSBpZmVsc2UoY2xzZl9yZW9yZCRCaXRld29yayA9PSJZZXMiIHwgY2xzZl9yZW9yZCRCaXRld29yayA9PSAibm8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b2xvd2VyKGNsc2ZfcmVvcmQkQml0ZXdvcmspLCBjbHNmX3Jlb3JkJEJpdGV3b3JrKQojIENoYW5naW5nIEJpdGV3b3JrIHRvIG9yZGVyZWQgZmFjdG9yClNsaWNlck1vcnBoLnJlcGMkQml0ZXdvcmsgPC0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkQml0ZXdvcmssIG9yZGVyZWQgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygieWVzIiwgIm5vIiwgIk5BVFVSQUwiLCAiRk9YIikpCiMgUHV0dGluZyBjbHVzdGVyIGxldmVscyBpbnRvIGEgY29sdW1uIGFzIGEgZmFjdG9yClNsaWNlck1vcnBoLnJlcGMkY2x1c3RlciA8LSBmYWN0b3Ioa20kY2x1c3RlcikKCiMgTm93IHRvIGVhc2Ugdmlld2luZyB3ZSdsbCBmYWN0b3IgdGhlbSBpbnRvIGJyb2FkIGNhdGVnb3JpZXMKCiMgVHVybmluZyBVS0MgaXNudG8gYW4gb3JkZXJlZCBmYWN0b3IgY29sdW1uClNsaWNlck1vcnBoLnJlcGMkVUtDLnNoYXBlZmFjdCA8LSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRVS0MsIG9yZGVyZWQgPSBULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBVS0NfZ3JvdXBfb3JkZXIpCgojIyBBS0MgbmV4dApTbGljZXJNb3JwaC5yZXBjJEFLQy5zaGFwZWZhY3QgPC0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkQUtDLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBBS0NfZ3JvdXBfb3JkZXIpCgpTbGljZXJNb3JwaC5yZXBjJEFLQy5mYWN0IDwtIGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJEFLQykKCiMjIFNldHRpbmcgdXAgYSBjb2x1bW4gdGhhdCBpcyBkb21lc3RpYyAoZG9ncykgYW5kIG5hdHVyYWwgKGZveGVzLCBvdGhlcnMpClNsaWNlck1vcnBoLnJlcGMkZG9tZXN0aWMgPC0gZmFjdG9yKGlmZWxzZShTbGljZXJNb3JwaC5yZXBjJFVLQyA9PSAiTkFUVVJBTCIgfCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2xpY2VyTW9ycGgucmVwYyRVS0MgPT0gIkZPWCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5hdHVyYWwiLCAiRG9tZXN0aWNhdGVkIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwgbGV2ZWxzID0gYygiRG9tZXN0aWNhdGVkIiwgIk5hdHVyYWwiKSkKCiMjIENhbGN1bGF0aW5nIGdyb3VwIG1lYW5zIGZvciBzZXR0aW5nIHNrdWxscyBvbiBwbG90czogClVLQyA8LSBTbGljZXJNb3JwaC5yZXBjWyFpcy5uYShTbGljZXJNb3JwaC5yZXBjJFVLQyksXQpVS0MgPC0gVUtDWyxjKCJQQy4xIiwgIlBDLjIiLCAiVUtDIildClVLQ19zY2VudGhvdW5kX21lYW4gPC0gZGF0YS5mcmFtZSh4ID0gbWVhbihVS0MkUEMuMVtVS0MkVUtDID09ICJTY2VudGhvdW5kIl0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbWVhbihVS0MkUEMuMltVS0MkVUtDID09ICJTY2VudGhvdW5kIl0pKQpVS0NfY29tcGFuaW9uX21lYW4gPC0gZGF0YS5mcmFtZSh4ID0gbWVhbihVS0MkUEMuMVtVS0MkVUtDID09ICJDb21wYW5pb24iXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBtZWFuKFVLQyRQQy4yW1VLQyRVS0MgPT0gIkNvbXBhbmlvbiJdKSkKQUtDIDwtIFNsaWNlck1vcnBoLnJlcGNbIWlzLm5hKFNsaWNlck1vcnBoLnJlcGMkQUtDKSxdCkFLQyA8LSBBS0NbLGMoIlBDLjEiLCAiUEMuMiIsICJBS0MiKV0KQUtDX2hvdW5kX21lYW4gPC0gZGF0YS5mcmFtZSh4ID0gbWVhbihBS0MkUEMuMVtBS0MkQUtDID09ICJIb3VuZCJdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbWVhbihBS0MkUEMuMltBS0MkQUtDID09ICJIb3VuZCJdKSkKQUtDX25vbnNwb3J0aW5nX21lYW4gPC0gZGF0YS5mcmFtZSh4ID0gbWVhbihBS0MkUEMuMVtBS0MkQUtDID09ICJOb24tc3BvcnRpbmciXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG1lYW4oQUtDJFBDLjJbQUtDJEFLQyA9PSAiTm9uLXNwb3J0aW5nIl0pKQpBS0NfdG95X21lYW4gPC0gZGF0YS5mcmFtZSh4ID0gbWVhbihBS0MkUEMuMVtBS0MkQUtDID09ICJUb3kiXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG1lYW4oQUtDJFBDLjJbQUtDJEFLQyA9PSAiVG95Il0pKQpgYGAKCkstbWVhbnMgY2x1c3RlcmluZyBwbG90OiAKCmBgYHtyIGstbWVhbnMsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9IkstbWVhbnMgY2x1c3RlcmluZyBwbG90LiJ9CiNub3cgYnVpbGQgdGhlIHBsb3QKcGttZWFuIDwtIGdncGxvdChTbGljZXJNb3JwaC5yZXBjWyFpcy5uYShTbGljZXJNb3JwaC5yZXBjJFVLQyksXSwgYWVzKFBDLjEsIFBDLjIsIGNvbG9yID0gY2x1c3RlciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGNsdXN0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBkb21lc3RpYykpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIHN0YXRfZWxsaXBzZShnZW9tID0gInBvbHlnb24iLCBhZXMoZmlsbCA9IGNsdXN0ZXIsIGdyb3VwID0gY2x1c3RlciksIGxldmVsID0gMC45NSwgYWxwaGEgPSAwLjEpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYyg1LCAxOSksIG5hbWUgPSAiICIpICsKICBzY2FsZV9jb2xvcl9tdW5jaChjaG9pY2UgPSAiTmlldHpzY2hlIiwgZGlzY3JldGUgPSBUUlVFLG5hbWUgPSAiQ2x1c3RlciIpICsgCiAgc2NhbGVfZmlsbF9tdW5jaChjaG9pY2UgPSAiTmlldHpzY2hlIiwgZGlzY3JldGUgPSBUUlVFLCBuYW1lID0gIkNsdXN0ZXIiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsID0gU2xpY2VyTW9ycGgucmVwYyRCcmVlZFshaXMubmEoU2xpY2VyTW9ycGgucmVwYyRVS0MpXSwgbWF4Lm92ZXJsYXBzID0gNCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkrCiAgeGxhYigiUEMgMSAoVmFyID0gNTAuMiUpIikgKyB5bGFiKCJQQyAyIChWYXIgPSAxMy4zJSkiKSArCiAgdGhlbWVfYncoKQpwa21lYW4KYGBgCgpLZW5uZWwtY2x1YiBncm91cGluZ3MgYW5kIFBDIHJlc3VsdHM6CgpgYGB7ciBrYy1ncm91cHMsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9IkstbWVhbnMgY2x1c3RlcmluZyBwbG90LiIsIGZpZy5oZWlnaHQ9NS41LCBmaWcud2lkdGg9MTJ9CiNub3cgYnVpbGQgdGhlIHBsb3QKa2NfcGFsZXR0ZSA8LSBtdW5jaF9wYWxldHRlKCJNdXJkZXJlciIsIDgpCmtjX3BhbGV0dGUgPC0gYyhrY19wYWxldHRlLCBrY19wYWxldHRlWzJdKQpwVUtDIDwtIGdncGxvdChTbGljZXJNb3JwaC5yZXBjWyFpcy5uYShTbGljZXJNb3JwaC5yZXBjJFVLQyksXSwgYWVzKFBDLjEsIFBDLjIsIGNvbG9yID0gVUtDLnNoYXBlZmFjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFVLQy5zaGFwZWZhY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBVS0Muc2hhcGVmYWN0KSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgc3RhdF9lbGxpcHNlKGdlb20gPSAicG9seWdvbiIsIGFlcyhncm91cCA9IFVLQy5zaGFwZWZhY3QpLCBsZXZlbCA9IDAuOTUsIGFscGhhID0gMC4yKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTksIDAsIDEsIDIsIDUsIDYsIDcsIDksIDEwKSwgbmFtZSA9ICJVS0MgR3JvdXBzIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBrY19wYWxldHRlLCBuYW1lID0gIlVLQyBHcm91cHMiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGtjX3BhbGV0dGUsIG5hbWUgPSAiVUtDIEdyb3VwcyIpICsKICBnZW9tX3RleHRfcmVwZWwobGFiZWwgPSBTbGljZXJNb3JwaC5yZXBjJEJyZWVkWyFpcy5uYShTbGljZXJNb3JwaC5yZXBjJFVLQyldLCAKICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IDAuMSwgeGVuZCA9IFVLQ19jb21wYW5pb25fbWVhbiR4LCB5ID0gLTAuMSwgeWVuZCA9IFVLQ19jb21wYW5pb25fbWVhbiR5LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSBrY19wYWxldHRlWzRdKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gLTAuMjQsIHhlbmQgPSBVS0Nfc2NlbnRob3VuZF9tZWFuJHgsIHkgPSAtMC4xLCB5ZW5kID0gVUtDX3NjZW50aG91bmRfbWVhbiR5LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSBrY19wYWxldHRlWzldKSArCiAgeGxpbSgtMC4zMSwgTkEpICsKICB5bGltKC0wLjIwLCBOQSkgKwogIHhsYWIoIlBDIDEgKFZhciA9IDUwLjIlKSIpICsgeWxhYigiUEMgMiAoVmFyID0gMTMuMyUpIikgKwogIHRoZW1lX2J3KCkKcEFLQyA8LSBnZ3Bsb3QoU2xpY2VyTW9ycGgucmVwY1shaXMubmEoU2xpY2VyTW9ycGgucmVwYyRBS0MpLF0sIGFlcyhQQy4xLCBQQy4yLCBjb2xvciA9IEFLQy5zaGFwZWZhY3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBBS0Muc2hhcGVmYWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gQUtDLnNoYXBlZmFjdCkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIHN0YXRfZWxsaXBzZShnZW9tID0gInBvbHlnb24iLCBhZXMoZmlsbCA9IEFLQy5zaGFwZWZhY3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBBS0Muc2hhcGVmYWN0KSwgbGV2ZWwgPSAwLjk1LCBhbHBoYSA9IDAuMikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE5LCAwLCAxLCAyLCA1LCA2LCA3LCA5LCAxMCksIG5hbWUgPSAiQUtDIEdyb3VwcyIpICsKICBzY2FsZV9jb2xvcl9tdW5jaChjaG9pY2UgPSAiTXVyZGVyZXIiLCBkaXNjcmV0ZSA9IFRSVUUsIG5hbWUgPSAiQUtDIEdyb3VwcyIpICsgCiAgc2NhbGVfZmlsbF9tdW5jaChjaG9pY2UgPSAiTXVyZGVyZXIiLCBkaXNjcmV0ZSA9IFRSVUUsIG5hbWUgPSAiQUtDIEdyb3VwcyIpICsKICBnZW9tX3RleHRfcmVwZWwobGFiZWwgPSBTbGljZXJNb3JwaC5yZXBjJEJyZWVkWyFpcy5uYShTbGljZXJNb3JwaC5yZXBjJEFLQyldLCBtYXgub3ZlcmxhcHMgPSA0LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAtMC4yMiwgeGVuZCA9IEFLQ19ob3VuZF9tZWFuJHgsIHkgPSAtMC4wNSwgeWVuZCA9IEFLQ19ob3VuZF9tZWFuJHksCiAgICAgICAgICAgICBsdHkgPSAxLCBjb2xvciA9IGtjX3BhbGV0dGVbOF0pICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAwLjEsIHhlbmQgPSBBS0Nfbm9uc3BvcnRpbmdfbWVhbiR4LCB5ID0gMC4xNSwgeWVuZCA9IEFLQ19ub25zcG9ydGluZ19tZWFuJHksCiAgICAgICAgICAgICBsdHkgPSAxLCBjb2xvciA9IGtjX3BhbGV0dGVbN10pICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAwLjE2LCB4ZW5kID0gQUtDX3RveV9tZWFuJHgsIHkgPSAtMC4wNywgeWVuZCA9IEFLQ190b3lfbWVhbiR5LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSBrY19wYWxldHRlWzRdKSArCiAgeGxpbSgtMC4zMSwgTkEpICsKICB5bGltKE5BLCAwLjIwKSArCiAgeGxhYigiUEMgMSAoVmFyID0gNTAuMiUpIikgKyB5bGFiKCJQQyAyIChWYXIgPSAxMy4zJSkiKSArCiAgdGhlbWVfYncoKSAKCnBVS0MgKyBwQUtDCmBgYAoKCiMjIERpc2NyaW1pbmFudCBGYWN0b3JzIGJldHdlZW4gZ3JvdXBzCgpDb21wYXJpc29uIG9mIGxhbmRtYXJrcyBiZXR3ZWVuIGdyb3VwIG1lYW5zCgpgYGB7cn0KIyBFeHRyYWN0aW5nIGZpcnN0IDUgUENzIGFuZCBlYWNoIGtlbm5lbC1jbHViIGdyb3VwaW5nIHNjaGVtZQpza3VsbHMgPC0gY2JpbmQoU2xpY2VyTW9ycGgucmVwY1ssIDE6NV0sIAogICAgICAgICAgICAgICAgIlVLQyIgPSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRVS0MsIG9yZGVyZWQgPSBULCBsZXZlbHMgPSBVS0NfZ3JvdXBfb3JkZXIpLCAKICAgICAgICAgICAgICAgICJBS0MiID0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkQUtDLCBvcmRlcmVkID0gVCwgbGV2ZWxzID0gQUtDX2dyb3VwX29yZGVyKSwKICAgICAgICAgICAgICAgICJiaXRld29yayIgPSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRCaXRld29yayksCiAgICAgICAgICAgICAgICAic2NlbnR3b3JrIiA9IGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJE5vc2V3b3JrKSkKc2t1bGxzIDwtIG5hLm9taXQoc2t1bGxzKQoKIyBDcmVhdGUgYSBwYWlyZWQgYW5kIG9yZGVyZWQgR1BBLWdyb3VwaW5nIG9iamVjdAptaXhkcm9wPC1ncGEkY29vcmRzWywsb3JkZXIoZGltbmFtZXMoZ3BhJGNvb3JkcylbWzNdXSldCm1peGRyb3A8LW1peGRyb3BbLCwtOTZdCgpyZWZvcmdlPC1nZW9tb3JwaC5kYXRhLmZyYW1lKHNoYXBlID0gbWl4ZHJvcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVS0MgPSBuYS5vbWl0KGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJFVLQywgb3JkZXJlZCA9IFQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBS0MgPSBuYS5vbWl0KGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJEFLQywgb3JkZXJlZCA9IFQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiaXRld29yayA9IG5hLm9taXQoZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkQml0ZXdvcmtbLTk2XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjZW50d29yayA9IG5hLm9taXQoZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkTm9zZXdvcmtbLTk2XSkpKQoKIyBDcmVhdGUgYSBmdW5jdGlvbiB0aGF0IHN1YnNldHMgdGhlIG1lYW4gbGFuZG1hcmtzIGZvciBlYWNoIGdyb3VwIGFuZCBkb2VzIGEgcGFpcndpc2Ugc3VidHJhY3Rpb24gYW5kIHNxdWFyaW5nIG9mIHRob3NlIG1lYW5zCm1lYW5mb3JnZSA8LSBmdW5jdGlvbih0YXJnZXQsIGdyb3VwKSB7CiAgI3N1Yi1zZXR0aW5nIHRoZSBtZWFucyBhbmQgc2VuZGluZyB0aGVtIHRvIGEgbmV3IG9iamVjdAogIG5ldy5jb29yZHM8LWNvb3Jkcy5zdWJzZXQodGFyZ2V0W1sic2hhcGUiXV0sIGdyb3VwID0gdGFyZ2V0W1tncm91cF1dKQogIG91dHB1dF9tZWFuczwtbGFwcGx5KG5ldy5jb29yZHMsIG1zaGFwZSkKICAjcGVyZm9ybSB0aGUgc3VidHJhY3Rpb24gYW5kIHNxdWFyaW5nIG9mIHRoZSBwYWlyd2lzZSBjb21iaW5hdGlvbnMKICByZXN1bHQ8LSBjb21ibihvdXRwdXRfbWVhbnMsIDIsIGZ1bmN0aW9uKHgpICh4W1sxXV0teFtbMl1dKV4yLCBzaW1wbGlmeSA9IEZBTFNFKQogICNyZW5hbWUgdGhlIHBhaXJzIHNvIHRoZXkncmUgcmVhZGFibGUKICBuYW1lcyhyZXN1bHQpPC1jb21ibihuYW1lcyhvdXRwdXRfbWVhbnMpLCAyLCBmdW5jdGlvbihuKSBwYXN0ZShuWzFdLCAiLSIsIG5bMl0pLCBzaW1wbGlmeSA9IFRSVUUpCiAgcmV0dXJuKHJlc3VsdCkKfQoKI25vdyBydW4gdGhlIGZ1bmN0aW9uIGZvciBlYWNoIGdyb3VwaW5nIHNjaGVtZS4gRWFjaCBtZW1iZXIgb2YgdGhlIGxpc3Qgd2lsbCBiZSBuYW1lZCBiYXNlZCBvbiB3aGljaCB0d28gZ3JvdXBzIGFyZSBjb21wYXJlZAptZWFuc2hhcGUuVUtDPC1tZWFuZm9yZ2UocmVmb3JnZSwiVUtDIikKbWVhbnNoYXBlLkFLQzwtbWVhbmZvcmdlKHJlZm9yZ2UsIkFLQyIpCm1lYW5zaGFwZS5ub3NlPC1tZWFuZm9yZ2UocmVmb3JnZSwic2NlbnR3b3JrIikKbWVhbnNoYXBlLmJpdGU8LW1lYW5mb3JnZShyZWZvcmdlLCJiaXRld29yayIpCgpgYGAKClJlZm9ybWF0dGluZyBkYXRhIGZvciB2aXN1YWxpemF0aW9uOgoKYGBge3J9CmNhbGNfZ3BhX2Rpc3QgPC0gZnVuY3Rpb24oZGF0KXsKICByZXF1aXJlKHRpZHlyKQogIG4gPC0gbGVuZ3RoKG5hbWVzKGRhdCkpCiAgcmVzdWx0cyA8LSBtYXRyaXgoTkEsIG5yb3cgPSBucm93KGRhdFtbMV1dKSwgbmNvbCA9IG4pCiAgcmVzdWx0cyA8LSBhcy5kYXRhLmZyYW1lKHJlc3VsdHMpCiAgcmVzdWx0cyA8LSBjYmluZChzZXEoMSxucm93KHJlc3VsdHMpKSwgcmVzdWx0cykKICBjb2xuYW1lcyhyZXN1bHRzKSA8LSBjKCJsYW5kbWFyayIsbmFtZXMoZGF0KSkKICAKICBmb3IoaSBpbiBuYW1lcyhkYXQpKXsKICAgIHJlc3VsdHNbW2ldXSA8LSBzcXJ0KGRhdFtbaV1dWywiWCJdXjIgKyBkYXRbW2ldXVssIlkiXV4yICsgZGF0W1tpXV1bLCJaIl1eMikKICB9CiAgcmVzdWx0c19sb25nIDwtIHBpdm90X2xvbmdlcihyZXN1bHRzLCBjb2xzID0gLSJsYW5kbWFyayIsIG5hbWVzX3RvID0gImNvbXBhcmlzb24iKQogIHJlc3VsdHNfbG9uZyRsYW5kbWFyayA8LSBmYWN0b3IocmVzdWx0c19sb25nJGxhbmRtYXJrKQogIHJldHVybihyZXN1bHRzX2xvbmcpCn0KCm1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSA8LSBjYWxjX2dwYV9kaXN0KG1lYW5zaGFwZS5BS0MpCm1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSA8LSBjYWxjX2dwYV9kaXN0KG1lYW5zaGFwZS5VS0MpCm1lYW5zaGFwZS5iaXRlW1sicGxvdHRpbmciXV0gPC0gY2FsY19ncGFfZGlzdChtZWFuc2hhcGUuYml0ZSkKbWVhbnNoYXBlLmJpdGVbWyJjdXRvZmYiXV0gPC0gZGF0YS5mcmFtZSgibWVhbiIgPSBtZWFuKG1lYW5zaGFwZS5iaXRlW1sicGxvdHRpbmciXV0kdmFsdWUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZCIgPSBzZChtZWFuc2hhcGUuYml0ZVtbInBsb3R0aW5nIl1dJHZhbHVlKSkKbWVhbnNoYXBlLm5vc2VbWyJwbG90dGluZyJdXSA8LSBjYWxjX2dwYV9kaXN0KG1lYW5zaGFwZS5ub3NlKQptZWFuc2hhcGUubm9zZVtbImN1dG9mZiJdXSA8LSBkYXRhLmZyYW1lKCJtZWFuIiA9IG1lYW4obWVhbnNoYXBlLm5vc2VbWyJwbG90dGluZyJdXSR2YWx1ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNkIiA9IHNkKG1lYW5zaGFwZS5ub3NlW1sicGxvdHRpbmciXV0kdmFsdWUpKQoKbWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJHNob3J0IDwtIGlmZWxzZShncmVwbCgidG95IiwgbWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJGNvbXBhcmlzb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidG95IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIm5hdHVyYWwiLCBtZWFuc2hhcGUuQUtDW1sicGxvdHRpbmciXV0kY29tcGFyaXNvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IFQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYXR1cmFsIiwgIm90aGVyIikpCm1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSRzaG9ydCA8LSBmYWN0b3IobWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJHNob3J0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwgbGV2ZWxzID0gYygidG95IiwgIm5hdHVyYWwiLCAib3RoZXIiKSkKbWVhbnNoYXBlLkFLQ1tbImN1dG9mZiJdXSA8LSBkYXRhLmZyYW1lKCJtZWFuIiA9IG1lYW4obWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJHZhbHVlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2QiID0gc2QobWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJHZhbHVlKSkKbWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJHNob3J0IDwtIGlmZWxzZShncmVwbCgiY29tcGFuaW9uIiwgbWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJGNvbXBhcmlzb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29tcGFuaW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIm5hdHVyYWwiLCBtZWFuc2hhcGUuVUtDW1sicGxvdHRpbmciXV0kY29tcGFyaXNvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYXR1cmFsIiwgIm90aGVyIikpCm1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSRzaG9ydCA8LSBmYWN0b3IobWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJHNob3J0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwgbGV2ZWxzID0gYygiY29tcGFuaW9uIiwgIm5hdHVyYWwiLCAib3RoZXIiKSkKbWVhbnNoYXBlLlVLQ1tbImN1dG9mZiJdXSA8LSBkYXRhLmZyYW1lKCJtZWFuIiA9IG1lYW4obWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJHZhbHVlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2QiID0gc2QobWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJHZhbHVlKSkKCmBgYAoKQUtDICYgVUtDIGdyb3VwczoKYGBge3J9CnBfVUtDX2NvbXBhcmUgPC0gZ2dwbG90KG1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSwgYWVzKGxhbmRtYXJrLCB2YWx1ZSwgY29sb3IgPSBzaG9ydCkpICsgCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMSkpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbnNoYXBlLlVLQ1tbImN1dG9mZiJdXSRtZWFuKzIqbWVhbnNoYXBlLlVLQ1tbImN1dG9mZiJdXSRzZCwKICAgICAgICAgICAgIGx0eSA9IDIpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobmFtZSA9ICJDb21wYXJpc29uXG5hZ2FpbnN0OiIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAxLCB4ZW5kID0gMjAsIHkgPSAwLjAwODUsIHllbmQgPSAwLjAwODUsCiAgICAgICAgICAgICBsdHkgPSAxLCBjb2xvciA9ICJyZWQiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTAsIHkgPSAwLjAwNzUsIGxhYmVsID0gIkRvcnNhbCBmZWF0dXJlcyIsIGNvbG9yID0gInJlZCIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAyMSwgeGVuZCA9IDM4LCB5ID0gMC4wMDg1LCB5ZW5kID0gMC4wMDg1LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSAiYmx1ZSIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAzMCwgeSA9IDAuMDA3NSwgbGFiZWwgPSAiVmVudHJhbCBmZWF0dXJlcyIsIGNvbG9yID0gImJsdWUiKSArCiAgeWxhYigiR3JvdXAtbWVhbiBsYW5kbWFya1xuZGlzdGFuY2Ugc3F1YXJlZCIpICsKICBnZ3RpdGxlKCJVS0MgZ3JvdXAgY29tcGFyaXNvbnMiKSArCiAgdGhlbWVfYncoKSAjKwogICN0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICAjdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImluc2lkZSIsIGxlZ2VuZC5wb3NpdGlvbi5pbnNpZGUgPSBjKDAuNzUsMC42NSksCiAgIyAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImdyYXkzMCIsIGZpbGwgPSAid2hpdGUiLCBsaW5ldHlwZT0ic29saWQiLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ld2lkdGggPSAwLjIpKQoKcF9BS0NfY29tcGFyZSA8LSBnZ3Bsb3QobWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dLCBhZXMobGFuZG1hcmssIHZhbHVlLCBjb2xvciA9IHNob3J0KSkgKyAKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xKSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuc2hhcGUuQUtDW1siY3V0b2ZmIl1dJG1lYW4rMiptZWFuc2hhcGUuQUtDW1siY3V0b2ZmIl1dJHNkLAogICAgICAgICAgICAgbHR5ID0gMikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChuYW1lID0gIkNvbXBhcmlzb25cbmFnYWluc3Q6IikgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IDEsIHhlbmQgPSAyMCwgeSA9IDAuMDA4NSwgeWVuZCA9IDAuMDA4NSwKICAgICAgICAgICAgIGx0eSA9IDEsIGNvbG9yID0gInJlZCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxMCwgeSA9IDAuMDA3NSwgbGFiZWwgPSAiRG9yc2FsIGZlYXR1cmVzIiwgY29sb3IgPSAicmVkIikgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IDIxLCB4ZW5kID0gMzgsIHkgPSAwLjAwODUsIHllbmQgPSAwLjAwODUsCiAgICAgICAgICAgICBsdHkgPSAxLCBjb2xvciA9ICJibHVlIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDMwLCB5ID0gMC4wMDc1LCBsYWJlbCA9ICJWZW50cmFsIGZlYXR1cmVzIiwgY29sb3IgPSAiYmx1ZSIpICsKICB5bGFiKCJHcm91cC1tZWFuIGxhbmRtYXJrXG5kaXN0YW5jZSBzcXVhcmVkIikgKwogIGdndGl0bGUoIkFLQyBncm91cCBjb21wYXJpc29ucyIpICsKICB0aGVtZV9idygpICMrCiAgI3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQogICAgI3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJpbnNpZGUiLCBsZWdlbmQucG9zaXRpb24uaW5zaWRlID0gYygwLjc1LDAuNjUpLAogICAgIyAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmF5MzAiLCBmaWxsID0gIndoaXRlIiwgbGluZXR5cGU9InNvbGlkIiwKICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXdpZHRoID0gMC4yKSkKcF9BS0NfY29tcGFyZSAvIHBfVUtDX2NvbXBhcmUKYGBgCk5vc2V3b3JrIGFuZCBCaXRld29yazogCgpgYGB7cn0KcF9iaXRlX2NvbXBhcmUgPC0gIGdncGxvdChtZWFuc2hhcGUuYml0ZVtbInBsb3R0aW5nIl1dLCBhZXMobGFuZG1hcmssIHZhbHVlLCBjb2xvciA9IGNvbXBhcmlzb24pKSArIAogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjEpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW5zaGFwZS5iaXRlW1siY3V0b2ZmIl1dJG1lYW4rMiptZWFuc2hhcGUuYml0ZVtbImN1dG9mZiJdXSRzZCwKICAgICAgICAgICAgIGx0eSA9IDIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAxLCB4ZW5kID0gMjAsIHkgPSAwLjAwNywgeWVuZCA9IDAuMDA3LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSAicmVkIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEwLCB5ID0gMC4wMDY1LCBsYWJlbCA9ICJEb3JzYWwgZmVhdHVyZXMiLCBjb2xvciA9ICJyZWQiKSArCiAgICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAyMSwgeGVuZCA9IDM4LCB5ID0gMC4wMDcsIHllbmQgPSAwLjAwNywKICAgICAgICAgICAgIGx0eSA9IDEsIGNvbG9yID0gImJsdWUiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMzAsIHkgPSAwLjAwNjUsIGxhYmVsID0gIlZlbnRyYWwgZmVhdHVyZXMiLCBjb2xvciA9ICJibHVlIikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChuYW1lID0gIkdyb3VwLWdyb3VwXG5jb21wYXJpc29uIikgKwogIHlsYWIoIkdyb3VwLW1lYW4gbGFuZG1hcmtcbmRpc3RhbmNlIHNxdWFyZWQiKSArCiAgZ2d0aXRsZSgiQml0ZS13b3JrIGdyb3VwIGNvbXBhcmlzb25zIikgKwogIHRoZW1lX2J3KCkKcF9ub3NlX2NvbXBhcmUgPC0gZ2dwbG90KG1lYW5zaGFwZS5ub3NlW1sicGxvdHRpbmciXV0sIGFlcyhsYW5kbWFyaywgdmFsdWUsIGNvbG9yID0gY29tcGFyaXNvbikpICsgCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMSkpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbnNoYXBlLm5vc2VbWyJjdXRvZmYiXV0kbWVhbisyKm1lYW5zaGFwZS5ub3NlW1siY3V0b2ZmIl1dJHNkLAogICAgICAgICAgICAgbHR5ID0gMikgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IDEsIHhlbmQgPSAyMCwgeSA9IDAuMDA3LCB5ZW5kID0gMC4wMDcsCiAgICAgICAgICAgICBsdHkgPSAxLCBjb2xvciA9ICJyZWQiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTAsIHkgPSAwLjAwNjUsIGxhYmVsID0gIkRvcnNhbCBmZWF0dXJlcyIsIGNvbG9yID0gInJlZCIpICsKICAgIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IDIxLCB4ZW5kID0gMzgsIHkgPSAwLjAwNywgeWVuZCA9IDAuMDA3LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSAiYmx1ZSIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAzMCwgeSA9IDAuMDA2NSwgbGFiZWwgPSAiVmVudHJhbCBmZWF0dXJlcyIsIGNvbG9yID0gImJsdWUiKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG5hbWUgPSAiR3JvdXAtZ3JvdXBcbmNvbXBhcmlzb24iKSArCiAgeWxhYigiR3JvdXAtbWVhbiBsYW5kbWFya1xuZGlzdGFuY2Ugc3F1YXJlZCIpICsKICBnZ3RpdGxlKCJTY2VudC13b3JrIGdyb3VwIGNvbXBhcmlzb25zIikgKwogIHRoZW1lX2J3KCkKCnBfYml0ZV9jb21wYXJlIC8gcF9ub3NlX2NvbXBhcmUgKyBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpCmBgYAoKIyMgTUFOT1ZBIAoKYGBge3J9Cm1hbl9VS0MgPC0gbWFub3ZhKGFzLm1hdHJpeChza3VsbHNbLDE6NV0pfnNrdWxscyRVS0MpCnN1bW1hcnkuYW92KG1hbl9VS0MsIHRlc3Q9IlBpbGxhaSIpCgojdXNpbmcgR2VvbW9ycGggYW5kIGRvaW5nIHBhaXJ3aXNlIGNvbXBhcmlzb25zCm1hblVLQzwtcHJvY0QubG0oc2hhcGUgfiBVS0MsIGRhdGEgPSByZWZvcmdlLCBSUlBQID0gVFJVRSkKbWFuQUtDPC1wcm9jRC5sbShzaGFwZSB+IEFLQywgZGF0YSA9IHJlZm9yZ2UsIFJSUFAgPSBUUlVFKQptYW5CaXRlPC1wcm9jRC5sbShzaGFwZSB+IGJpdGV3b3JrLCBkYXRhID0gcmVmb3JnZSwgUlJQUCA9IFRSVUUpCm1hblNjZW50PC1wcm9jRC5sbShzaGFwZSB+IHNjZW50d29yaywgZGF0YSA9IHJlZm9yZ2UsIFJSUFAgPSBUUlVFKQoKaG9sZC51a2M8LW1hbm92YShtYW5VS0MpCmhvbGQuYWtjPC1tYW5vdmEobWFuQUtDKQpob2xkLmJpdGU8LW1hbm92YShtYW5CaXRlKQpob2xkLm5vc2U8LW1hbm92YShtYW5TY2VudCkKCnVrY3BhaXJzPC1wYWlyd2lzZShtYW5VS0MsIGdyb3VwcyA9IHJlZm9yZ2UkVUtDKQpha2NwYWlyczwtcGFpcndpc2UobWFuQUtDLCBncm91cHMgPSByZWZvcmdlJEFLQykKYml0ZXBhaXJzPC1wYWlyd2lzZShtYW5CaXRlLCBncm91cHMgPSByZWZvcmdlJGJpdGV3b3JrKQpub3NlcGFpcnM8LXBhaXJ3aXNlKG1hblNjZW50LCBncm91cHMgPSByZWZvcmdlJHNjZW50d29yaykKCgpwdmFscy5VS0M8LXN1bW1hcnkodWtjcGFpcnMsIHRlc3QudHlwZT0gImRpc3QiLCBjb25maWRlbmNlID0gMC45NSwgc3RhdC50YWJsZSA9IFRSVUUpCnB2YWxzLlVLQzwtcHZhbHMuVUtDJHBhaXJ3aXNlLnRhYmxlcyRQW2xvd2VyLnRyaShwdmFscy5VS0MkcGFpcndpc2UudGFibGVzJFApXQpwdmFscy5BS0M8LXN1bW1hcnkoYWtjcGFpcnMsIHRlc3QudHlwZT0gImRpc3QiLCBjb25maWRlbmNlID0gMC45NSwgc3RhdC50YWJsZSA9IFRSVUUpCnB2YWxzLkFLQzwtcHZhbHMuQUtDJHBhaXJ3aXNlLnRhYmxlcyRQW2xvd2VyLnRyaShwdmFscy5BS0MkcGFpcndpc2UudGFibGVzJFApXQpwdmFscy5iaXRlPC1zdW1tYXJ5KGJpdGVwYWlycywgdGVzdC50eXBlPSAiZGlzdCIsIGNvbmZpZGVuY2UgPSAwLjk1LCBzdGF0LnRhYmxlID0gVFJVRSkKcHZhbHMuYml0ZTwtcHZhbHMuYml0ZSRwYWlyd2lzZS50YWJsZXMkUFtsb3dlci50cmkocHZhbHMuYml0ZSRwYWlyd2lzZS50YWJsZXMkUCldCnB2YWxzLm5vc2U8LXN1bW1hcnkobm9zZXBhaXJzLCB0ZXN0LnR5cGU9ICJkaXN0IiwgY29uZmlkZW5jZSA9IDAuOTUsIHN0YXQudGFibGUgPSBUUlVFKQpwdmFscy5ub3NlPC1wdmFscy5ub3NlJHBhaXJ3aXNlLnRhYmxlcyRQW2xvd2VyLnRyaShwdmFscy5ub3NlJHBhaXJ3aXNlLnRhYmxlcyRQKV0KCnB2YWxzLnVrYy5hZGp1c3RlZDwtcC5hZGp1c3QocHZhbHMuVUtDLCBtZXRob2QgPSAiYm9uZiIpCnB2YWxzLmFrYy5hZGp1c3RlZDwtcC5hZGp1c3QocHZhbHMuQUtDLCBtZXRob2QgPSAiYm9uZiIpCnB2YWxzLmJpdGUuYWRqdXN0ZWQ8LXAuYWRqdXN0KHB2YWxzLmJpdGUsIG1ldGhvZCA9ICJib25mIikKcHZhbHMubm9zZS5hZGp1c3RlZDwtcC5hZGp1c3QocHZhbHMubm9zZSwgbWV0aG9kID0gImJvbmYiKQpgYGAKCmBgYHtyfQpsZGFfVUtDIDwtIE1BU1M6OmxkYShza3VsbHMkVUtDfmFzLm1hdHJpeChza3VsbHNbLDE6NV0pKQpsZGFfVUtDCmBgYAoKIyMgVGVzdGluZyBvZiBGdW5jdGlvbmFsIEdyb3VwcwoKYGBge3IgZnhuLWdyb3VwaW5ncywgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD0zLjUsIGZpZy5jYXA9IkJpdGV3b3JrIGFuZCBzY2VudCB3b3JrLiJ9CmZ4bl9ncm91cF9wYWxldHRlIDwtIG11bmNoX3BhbGV0dGUoIk11cmRlcmVyIiw4KVtjKDE6Myw1KV0KcGJpdGUgPC0gZ2dwbG90KFNsaWNlck1vcnBoLnJlcGMsIGFlcyhQQy4xLCBQQy4yLCBjb2xvciA9IEJpdGV3b3JrLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gQml0ZXdvcmssCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBCaXRld29yaykpICsgCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X2VsbGlwc2UoZ2VvbSA9ICJwb2x5Z29uIiwgbGV2ZWwgPSAwLjk1LCBhbHBoYSA9IDAuMSkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDIxLCAyMywgMCwgMiksIG5hbWUgPSAiICIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZnhuX2dyb3VwX3BhbGV0dGUsIG5hbWUgPSAiICIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZnhuX2dyb3VwX3BhbGV0dGUsIG5hbWUgPSAiICIpICsKICB4bGFiKCJQQyAxIChWYXIgPSA1MC4yJSkiKSArIAogIHlsYWIoIiAiKSArCiAgZ2d0aXRsZSgiU2VsZWN0ZWQgZm9yIGJpdGUgd29yayIpICsKICB0aGVtZV9idygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKcHNjZW50IDwtIGdncGxvdChTbGljZXJNb3JwaC5yZXBjLCBhZXMoUEMuMSwgUEMuMiwgY29sb3IgPSBOb3Nld29yaywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IE5vc2V3b3JrLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gTm9zZXdvcmspKSArIAogIGdlb21fcG9pbnQoKSArCiAgc3RhdF9lbGxpcHNlKGdlb20gPSAicG9seWdvbiIsIGxldmVsID0gMC45NSwgYWxwaGEgPSAwLjEpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygyMSwgMjMsIDAsIDIpLCBuYW1lID0gIiAiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGZ4bl9ncm91cF9wYWxldHRlLCBuYW1lID0gIiAiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGZ4bl9ncm91cF9wYWxldHRlLCBuYW1lID0gIiAiKSArCiAgeGxhYigiUEMgMSAoVmFyID0gNTAuMiUpIikgKyAKICB5bGFiKCJQQyAyIChWYXIgPSAxMy4zJSkiKSArCiAgZ2d0aXRsZSgiU2VsZWN0ZWQgZm9yIHNjZW50IHdvcmsiKSArCiAgdGhlbWVfYncoKSAKcHNjZW50ICsgcGJpdGUgKyBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpICYgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKCmBgYAoKYGBge3IgYml0ZS1mb3JjZSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYml0ZV9kYXRhIDwtIGRhdGEuZnJhbWUoIkJyZWVkIiA9IFNsaWNlck1vcnBoLnJlcGMkQnJlZWQsIAogICAgICAgICAgICAgICAgICAgICAgICAiQml0ZXdvcmsiID0gU2xpY2VyTW9ycGgucmVwYyRCaXRld29yaywgCiAgICAgICAgICAgICAgICAgICAgICAgICJEb21lc3RpYyIgPSBTbGljZXJNb3JwaC5yZXBjJGRvbWVzdGljLCAKICAgICAgICAgICAgICAgICAgICAgICAgIlVLQyIgPSBTbGljZXJNb3JwaC5yZXBjJFVLQywgCiAgICAgICAgICAgICAgICAgICAgICAgICJBS0MiID0gU2xpY2VyTW9ycGgucmVwYyRBS0MsCiAgICAgICAgICAgICAgICAgICAgICAgICJjYW5pbmUiID0gU2xpY2VyTW9ycGgucmVwYyRiZnFfY2FuaW5lLCAKICAgICAgICAgICAgICAgICAgICAgICAgImNhcm5hc3NpYWwiID0gU2xpY2VyTW9ycGgucmVwYyRiZnFfY2FybmFzc2lhbCkKYml0ZV9kYXRhX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKGJpdGVfZGF0YSwgY29scyA9IGMoImNhbmluZSIsImNhcm5hc3NpYWwiKSkKcGZvcmNlIDwtIGdncGxvdChiaXRlX2RhdGFfbG9uZywgYWVzKEJpdGV3b3JrLCB2YWx1ZSwgZmlsbCA9IG5hbWUpKSArIAogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNiwgb3V0bGllci5zaGFwZSA9IE5BKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2Uoaml0dGVyLndpZHRoID0gMC4yKSwgc2hhcGUgPSAyMSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG11bmNoX3BhbGV0dGUoIlllbGxvd0xvZyIsMiksIG5hbWUgPSAiICIpICsKICB5bGFiKCJCaXRlLWZvcmNlIHF1b3RpZW50IikgKyB4bGFiKCJTZWxlY3RlZCBmb3IgYml0ZSB3b3JrIikgKwogIHRoZW1lX2J3KCkKcGZvcmNlCmBgYAoKYGBge3IgYml0ZS1mb3JjZS1zdGF0c30KYml0ZV95ZXNfY2FuaW5lIDwtIGJpdGVfZGF0YSRjYW5pbmVbYml0ZV9kYXRhJEJpdGV3b3JrID09ICJ5ZXMiXQpiaXRlX25vX2NhbmluZSA8LSBiaXRlX2RhdGEkY2FuaW5lW2JpdGVfZGF0YSRCaXRld29yayA9PSAibm8iXQpzaGFwaXJvLnRlc3QoYml0ZV95ZXNfY2FuaW5lKQpzaGFwaXJvLnRlc3QoYml0ZV9ub19jYW5pbmUpCnQudGVzdChiaXRlX3llc19jYW5pbmUsIGJpdGVfbm9fY2FuaW5lKQoKYml0ZV95ZXNfY2FybmFzc2lhbCA8LSBiaXRlX2RhdGEkY2FybmFzc2lhbFtiaXRlX2RhdGEkQml0ZXdvcmsgPT0gInllcyJdCmJpdGVfbm9fY2FybmFzc2lhbCA8LSBiaXRlX2RhdGEkY2FybmFzc2lhbFtiaXRlX2RhdGEkQml0ZXdvcmsgPT0gIm5vIl0Kc2hhcGlyby50ZXN0KGJpdGVfeWVzX2Nhcm5hc3NpYWwpCnNoYXBpcm8udGVzdChiaXRlX25vX2Nhcm5hc3NpYWwpCnQudGVzdChiaXRlX3llc19jYXJuYXNzaWFsLCBiaXRlX25vX2Nhcm5hc3NpYWwpCgpiaXRlX25hdHVyYWxfY2FuaW5lIDwtIGJpdGVfZGF0YSRjYW5pbmVbYml0ZV9kYXRhJEJpdGV3b3JrID09ICJOQVRVUkFMIl0Kc2hhcGlyby50ZXN0KGJpdGVfbmF0dXJhbF9jYW5pbmUpCmJpdGVfZm94X2NhbmluZSA8LSBiaXRlX2RhdGEkY2FuaW5lW2JpdGVfZGF0YSRCaXRld29yayA9PSAiRk9YIl0Kc2hhcGlyby50ZXN0KGJpdGVfZm94X2NhbmluZSkKCnQudGVzdChiaXRlX3llc19jYW5pbmUsIGJpdGVfbmF0dXJhbF9jYW5pbmUpCnQudGVzdChiaXRlX3llc19jYW5pbmUsIGJpdGVfZm94X2NhbmluZSkKYGBgCgojIyBGb3IgdGhlIHBhcGVyOiAKCkZpZ3VyZSAyOiBrLW1lYW5zIGNsdXN0ZXIgb24gUEMgcGxvdAoKYGBge3IgZmlnLTIsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9IkEuIEstbWVhbnMgY2x1c3RlcmluZyBwbG90LiIsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTkuNn0KcGttZWFuICsgeGxpbSgtMC4yMiwgMC4yNSkgKyB0aGVtZShsZWdlbmQubWFyZ2luPW1hcmdpbihjKDAsMCwwLDApKSkKZ2dzYXZlKCIuL3Jlc3VsdHMvZmlndXJlcy9maWd1cmUyLnBkZiIsIGhlaWdodCA9IDQsIHdpZHRoID0gOS42KQpgYGAKCkZpZ3VyZSAzOiBVS0MgY29tcGFyaXNvbiBmaWd1cmUKCmBgYHtyIGZpZy0zLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSJBLiBLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdC4gQi4gQUtDIGdyb3VwaW5ncy4gQy4gVUtDIGdyb3VwaW5ncy4iLCBmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTEyfQpkZXNpZ24yIDwtIAogICJBQUFBCiAgIEFBQUEKICAgQkJCQiIKKHBVS0MgKSAvIAogIGZyZWUocF9VS0NfY29tcGFyZSArIGdndGl0bGUoIiAiKSArICAKICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSkpKSArIAogIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gIkEiKSArCiAgcGxvdF9sYXlvdXQoZGVzaWduID0gZGVzaWduMikgJiB0aGVtZShsZWdlbmQubWFyZ2luPW1hcmdpbihjKDAsMCwwLDApKSkKICAKZ2dzYXZlKCIuL3Jlc3VsdHMvZmlndXJlcy9maWd1cmUzLnBkZiIsIGhlaWdodCA9IDMuNDUqMiwgd2lkdGggPSAzLjI1KjIpCmBgYAoKRmlndXJlIDQ6IEFLQyBjb21wYXJpc29uIGZpZ3VyZQoKYGBge3IgZmlnLTQsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9IiBBS0MgZ3JvdXBpbmdzLiAiLCBmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTEyfQpkZXNpZ24yIDwtIAogICJBQUFBCiAgIEFBQUEKICAgQkJCQiIKcEFLQyAvIGZyZWUocF9BS0NfY29tcGFyZSArIGdndGl0bGUoIiAiKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpKSkrCiAgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAiQSIpICsKICBwbG90X2xheW91dChkZXNpZ24gPSBkZXNpZ24yKSAmIHRoZW1lKGxlZ2VuZC5tYXJnaW49bWFyZ2luKGMoMCwwLDAsMCkpKQpnZ3NhdmUoIi4vcmVzdWx0cy9maWd1cmVzL2ZpZ3VyZTQucGRmIiwgaGVpZ2h0ID0gMy40NSoyLCB3aWR0aCA9IDMuMjUqMikKYGBgCgpGaWd1cmUgNTogdGFzay1zcGVjaWZpYyBncm91cCByZXN1bHRzCgpgYGB7ciBmaWctNSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0iQS4gSy1tZWFucyBjbHVzdGVyaW5nIHBsb3QuIEIuIEFLQyBncm91cGluZ3MuIEMuIFVLQyBncm91cGluZ3MuIiwgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD0xMn0KZGVzaWduMiA8LSAKICAiQUFBQQogICBCQkJCCiAgIEJCQkIKICAgQ0NDQyIKKHBzY2VudCArIHBiaXRlICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSAmIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpKSAvIAogIChwX25vc2VfY29tcGFyZSAvcF9iaXRlX2NvbXBhcmUgKyAKICAgICBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpICYgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpKSAvIAogIHBmb3JjZSAgKyAKICBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICJBIikgKwogIHBsb3RfbGF5b3V0KGRlc2lnbiA9IGRlc2lnbjIpICYgdGhlbWUobGVnZW5kLm1hcmdpbj1tYXJnaW4oYygwLDAsMCwwKSkpCmdnc2F2ZSgiLi9yZXN1bHRzL2ZpZ3VyZXMvZmlndXJlNS5wZGYiLCB3aWR0aCA9IDkuNiwgaGVpZ2h0ID0gMTEuNTIpCmBgYAoKCgoKCgo=